/**
 * @file   Simlet.java
 * @author Enrique Madrid <e.madrid@lpcsite.com>
 * @date   Sat Jul 14 08:26:05 2007
 *
 * @brief  Applet.
 *
 */

package sim.framework;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.Vector;

import sim.*;
import sim.auth.DocAuth;
import sim.framework.io.File;
import sim.shell.*;
import sim.framework.gui.*;
import sim.framework.font.*;
import sim.utils.Utils;

import sim.utils.parser.*;

import sim.pref.ModPref;
import sim.auth.ModAuth;
import sim.time.ModTime;
import sim.sync.*;


/**
 * Applet.
 *
 * La clase que requiere implementar una solución sobre el framework SIMP debe extender
 * la clase Simlet.
 *
 * Esta clase mantiene una referencia de todos los objetos requeridos para la ejecuación
 * de modulos (Module), la administración de documentos (Document), y la interaccion con
 * el usuario (Gui).
 *
 * Dentro del modelo MVC (Model View Controler), los modelos de datos se implementan al
 * extender la clase Document, los controladores al extender la clase Module, y la vista
 * al crear una instancia de la clase Gui, Dialog, u otra derivada.
 *
 * @author Enrique J. Madrid M.
 * @version %I%, %I%
 * @since 1.0
 */
public class Simlet extends MIDlet implements EventListener {
    
    public static final int MOD_INIT = 0;  // Inicio del Simlet
    public static final int MOD_KILL = 1;  // Salir inesperadamente.
    public static final int MOD_FAIL = 2;  // Salir con mensaje de error.
    public static final int MOD_QUIT = 3;  // Salir del Simlet
    public static final int MOD_PREF = 4;  // Preferencias del Simlet
    public static final int MOD_AUTH = 5;  // Autenticacion
    public static final int MOD_EXIT = 6;  // Abandonar Autenticacion
    public static final int MOD_PASS = 7;  // Solicitar Password
    public static final int MOD_SYUP = 8;  // Actulizar Archivos
    public static final int MOD_SYCO = 9;  // Enviar Operaciones
    public static final int MOD_SYNC = 10; // Sincronizar
    public static final int MOD_MAIL = 11; // Mostrar Mensaje
    public static final int MOD_SUPP = 12; // Mantenimiento
    public static final int MOD_SUQU = 13; // Mantenimiento
    public static final int MOD_ABOT = 14; // Acerca de
    public static final int MOD_SHEL = 15; // Interfaz de comandos
    public static final int MOD_DEMO = 16; // Demo be happy!
    public static final int MOD_TIME = 17; // Time!
    public static final int MOD_USER = 50; // Modulos para el usuario.   
        
    protected FontSystem m_FontSys = null;
    protected Driver m_Driver = null;
    protected Worker m_Worker = null;
    protected Scheduler m_Scheduler = null;
    protected Renderer m_Renderer = null;
    protected Actions m_Actions = null;
    protected Documents m_Documents = null;
    protected Resource m_ResImage = null;
    protected Resource m_ResLabel = null;
    protected AppCfg m_AppCfg = null;
    protected String m_QuitMessage;
        
    // Indica que una instancia del applet se ha ejecutado.
    private boolean m_Started = false;
    
    // Indica que la instancia del Applet se ha inicializado.
    protected boolean m_InitOk = false;

    // Timeout de aplicación.
    private int m_AlarmId;
    private int m_TimeOut;
    
    // Contador del tiempo de inactividad
    private int m_TimeCount = 0;

    private Module m_Module = null;
    private Gui m_Gui = null;
    private Msg m_Msg = new Msg();

    public Simlet() {
	m_AlarmId = this.hashCode();
    }


    /**************************************************************
     *
     * FRAMEWORK
     * 
     **************************************************************/

    
    public final void setInitOk() {
        // Las clases que requiere este método deben ser amigas de Simlet.
        // por ejemplo, ModInit, la cambiaremos después. No hay tiempo!!!.
        m_InitOk = true;
    }
    
    public final boolean isInitOk() {
        return m_InitOk;
    }

    public final FontSystem getFontSys() {
        return m_FontSys;
    }
    
    protected void startApp() {
        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::startApp() ENTERED");
        //#endif
        
        if(!m_Started ) {
            //#ifdef DEBUG_SIMLET
            //# System.out.println("\tIniciando Simlet ...");
            //#endif
            
            // Configuración de la aplicación
            m_AppCfg = new AppCfg(this);
            
            // Iniciar el servidor de fuentes.
            m_FontSys = new FontSystem();
            
            // Iniciar el administrador de recursos etiquetas. (Soporte Multi-idiomas)
            m_ResLabel = new Resource(this);
            m_ResLabel.create("labels", Resource.TYPE_STRING, 0);
            
            // Iniciar el administrador de recursos gráficos. (Soporte Multi-idiotas)
            m_ResImage = new Resource(this);
            m_ResImage.create("icono", Resource.TYPE_IMAGE, 0);
            
            // Iniciar la cola de acciones éxito/fracaso (Don't worry be hacker!!!).
            m_Actions = new Actions(this);
            
            // Iniciar el administrador de documentos (Modelo MVC).
            m_Documents = new Documents();

            // Iniciar la hebra para despacho de eventos.
	    m_Driver = new Driver(this);

            // Iniciar la hebra para trabajo en segundo plano.
            m_Worker = new Worker(this);
            m_Worker.start();

            // Iniciar la hebra para programación de alarmas.
            m_Scheduler = new Scheduler(this);
            m_Scheduler.start();
            
            // Iniciar la hebra para efectos visuales.
            m_Renderer = new Renderer(this);
            m_Renderer.start();
            
            // The GNU KISS philosophy
            exec(createModule(70));

            m_Started = true;
            
            //#ifdef DEBUG_SIMLET
            //# System.out.println("\tSimlet iniciado!");
            //#endif
        }
        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::startApp() EXITING");
        //#endif
        
    }
    
    protected void pauseApp() {
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::pauseApp() ENTERED");
        //#endif
        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::pauseApp() EXITING");
        //#endif
    }
    
    protected void destroyApp(boolean unconditional) {
        Thread theCurrentThread;
        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::destroyApp() ENTERED");
        //#endif
        
        if (m_Started) {
            
            //#ifdef DEBUG_SIMLET
            //# System.out.println("\tterminando Simlet ...");
            //#endif
            
            // Detener la hebra para trabajo en segundo plano.
            m_Worker.stop();
            // Detener la hebra para programación de alarmas.
            m_Scheduler.stop();
            // Detener la hebra para efectos visuales.
            m_Renderer.stop();
            
            // Recuperando la hebra del rms.
            theCurrentThread = Thread.currentThread();
            try {
                // Esperando las hebras terminen ...
                theCurrentThread.sleep(500);
            } catch(InterruptedException ie) {
                //#ifdef DEBUG_SIMLET
                //# System.out.println("\tInterruptedException");
                //#endif
            } finally {
                
                // Liberar recursos del configuración de la aplicación
                m_AppCfg = null;
                
                // Liberar recursos del servidor de fuentes.
                m_FontSys = null;
                
                // Liberar recursos del administrador de recursos etiquetas. (Soporte Multi-idiomas)
                m_ResLabel = null;
                
                // Liberar recursos del recursos gráficos. (Soporte Multi-idiotas)
                m_ResImage = null;
                
                // Liberar recursos de la cola de acciones éxito/fracaso (Don't worry be hacker!!!).
                m_Actions = null;
                
                // Liberar recursos del administrador de documentos (Modelo MVC).
                m_Documents = null;

                // Liberar recursos de la hebra para despacho de eventos.
		m_Driver = null;

                // Liberar recursos de la hebra para trabajo en segundo plano.
                m_Worker = null;
                
                // Liberar recursos de la la hebra para programación de alarmas.
                m_Scheduler = null;
                
                // Liberar recursos de la hebra para efectos visuales.
                m_Renderer = null;
                
            }
            
            // Hasta la vista baby!!!
            m_Started = false;
            
            //#ifdef DEBUG_SIMLET
            //# System.out.println("\tSimlet terminado!");
            //#endif
            
        }
        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::destroyApp() EXITING");
        //#endif
        
    }



    /**************************************************************
     *
     * EVENTOS DEL SISTEMA
     * 
     **************************************************************/

    public boolean sendEvent(final Msg msg) {
	return event(msg);
    }

    public boolean postEvent(final Msg msg) {
	return m_Driver.postEvent(msg);
    }

    public boolean postEvent(int eventCode, int wParam, long dWParam) {
	m_Msg.setEventCode(eventCode);
	m_Msg.setWParam(wParam);
	m_Msg.setDWParam(dWParam);
	return postEvent(m_Msg);
    }

    public final boolean event(final Msg msg) {
	if (!dispatchEventSimlet(msg)) {
	    if (!dispatchEventModule(msg)) {
		if (dispatchEventGui(msg)) {
		    return false;
		}
	    }
	}
	return true;
    }

    private boolean dispatchEventSimlet(final Msg msg) {
	
        switch (msg.getEventCode()) {
	case EventListener.KEY_PRESSED:
	    m_TimeCount = 0;
	    break;
	}
	
	return dispatchEvent(msg);
    }

    private boolean dispatchEventModule(final Msg msg) {
	boolean ret = false;
	if (m_Module != null) {
	    ret = m_Module.event(msg);
	}
	return ret;
    }

    private boolean dispatchEventGui(final Msg msg) {
	boolean ret = false;
	if (m_Gui != null) {
	    switch (msg.getEventCode()) {
	    case EventListener.ALARM:
	    case EventListener.COMMAND:
		break;
	    default:
		ret = m_Gui.event(msg);
	    }
	}
	return true;
    }

    protected boolean dispatchEvent(final Msg msg) {
	return false;
    }

    /**************************************************************
     *
     * INTERFAZ GRÁFICA DE USUARIO
     * 
     **************************************************************/

    
    /** 
     * Establece la Interfaz Gráfica de Usuario a utilizar.
     * 
     * @param gui Una referencia aun objeto de la clase GUI.
     */
    protected  final void setGui(Gui gui) {        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::setGui() ENTERED");
        //#endif
	
        if (gui != null) {
	    gui.show();
	    Display.getDisplay(this).setCurrent(gui);
	}
	m_Gui = null;
	renderer(m_Gui);
	
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::setGui() EXITING");
        //#endif
    }

    protected final Gui getGui() {
	return m_Gui;
    }

    public void renderer(final Gui gui) {
	m_Renderer.set(gui);
    }    

    /**************************************************************
     *
     * ALARMAS
     * 
     **************************************************************/


    
    public boolean initAlarm(int id, long period, int param , EventListener e) {
	return m_Scheduler.initAlarm(id, period, param, e);
    }

    public void killAlarm(int id) {
	m_Scheduler.killAlarm(id);
    }


    /**************************************************************
     *
     * RECURSOS
     * 
     **************************************************************/


    /** 
     * Devuelve una imagen de administrador de Recursos.
     * 
     * @param id Idenfificador del recurso.
     * 
     * @return Si tiene éxito devuelve una referencia a un objeto del tipo Image.
     *         en caso contrario devuelve null.
     */
    public final Image getImage(int id) {
        return (Image)m_ResImage.getResource(id);
    }
    
    /** 
     * Devuelve una candena de caracteres del administrador de recursos.
     * 
     * @param id Identificador del recurso.
     * 
     * @return Si tiene éxito devuelve un referencia a un objeto del tipo String.
     *         en caso contrario devuelve null.
     */
    public final String getLabel( int id ) {
        return (String)m_ResLabel.getResource(id);
    }    

    
    /**************************************************************
     *
     * MODULOS
     * 
     **************************************************************/

    protected void work(Module module, int state) {
	m_Worker.append(module, state);
    }

    protected Module createModule( int mod_id ) {
        
        Module m = null;
        
        switch( mod_id ) {
	case Simlet.MOD_INIT:
	    // El Simlet se puede reiciar automáticamente.
	    m_Actions.delete();
	    // Ahora si, todo limpiecito.
	    m = new ModInit(mod_id, this);
	    break;
	case Simlet.MOD_QUIT:
	case Simlet.MOD_FAIL:
	case Simlet.MOD_KILL:
	    m = new ModQuit(mod_id, this);
	    break;
	case Simlet.MOD_PREF:
	    m = new ModPref(mod_id, this);
	    break;
	case Simlet.MOD_AUTH:
	    m = new ModAuth(mod_id, this);
	    break;
	case Simlet.MOD_EXIT:
	    m = new ModExit(mod_id, this);
	    break;
	case Simlet.MOD_PASS:
	    m = new ModPass(mod_id, this);
	    break;
	case Simlet.MOD_SYNC:
	    m = new ModSync(mod_id, this);
	    break;
	    // 	case Simlet.MOD_SYCO:
	    // 	    m = new ModSyncCommit(mod_id, this);
	    // 	    break;
	case Simlet.MOD_SYUP:
	    m = new ModSyncUpdate(mod_id, this);
	    break;
	    // 	case Simlet.MOD_MAIL:
	    // 	    m = new ModMail(mod_id, this);
	    // 	    break;
	    // 	case Simlet.MOD_SUPP:
	    // 	    m = new ModSupp(mod_id, this);
	    // 	    break;
	    // 	case Simlet.MOD_ABOT:
	    // 	    m = new ModSupp(mod_id, this);
	    // 	    break;
	case Simlet.MOD_TIME:
	    m = new ModTime(mod_id, this);
	    break;
	case Simlet.MOD_SHEL:
	    m = new ModShell(mod_id, this);
	    break;
	case Simlet.MOD_DEMO:
	    m = new ModDemo(mod_id, this);
	    break;
        }
        return m;
    }

    protected final void exec(Module module) {
	work(module, Executable.STATE_INIT);
    }

    /** 
     * Notifica al framework que la ejecución del módulo a fracasado.
     *
     * @see Simlet::execQueue()
     */    
    protected final void execBack() {
        work(m_Actions.remove(Executable.STATE_BACK), Executable.STATE_BACK);
    }
    
    /** 
     * Notifica al framework que la ejecución del módulo a tenido éxito.
     * 
     * @see Simlet::execQueue()
     */
    protected final void execNext() {
        work(m_Actions.remove(Executable.STATE_NEXT), Executable.STATE_NEXT);
    }

    /** 
     * Registra los módulos a ejecutar en caso de éxito o fracaso.
     *
     * Este método registra los módulos que serán ejecutados si un módulo 
     * notifica al framework que su ejecución ha tenido éxito o que ha
     * fracasado.
     * 
     * @param back Módulo a ejecutar en caso de fracaso.
     * @param next Módulo a ejecutar en caso de éxito.
     * @see Simlet::createModule()
     * @see Simlet::exec()
     */    
    protected final void execQueue(Module back, Module next) {
        m_Actions.append(back, next);
    }
        
    public final void execFail(final String message) {
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::execFail() ENTERED : " + message);
        //#endif
        // Establece una el contenido del mensaje en una variable de Simlet
        m_QuitMessage = message;
        
        // Ejecuta el modulo Fail
        exec(createModule(MOD_FAIL));
        
        //#ifdef DEBUG_SIMLET
        //# System.out.println("Simlet::execFail() EXITING");
        //#endif
        
    }
    
    /**
     * Establece el máximo tiempo que la aplicación puede permanecer si utilizar.
     *
     * @param timeout Valor en segundos que la aplicación puede permancer sin ser utilizada.
     *                este valor debe ser mayor que 60 segundos (1 minuto), en consideración
     *                al tiempo que puede demorar la aplicación en proceso de acceso Streams
     *                de datos de disco o la red. Un valor menor que 60 desactiva el control.
     */
    public final void setTimeOut(int timeout) {
        if (timeout > 60) {
            m_TimeOut = timeout;
            // Notificar al simlet cada 1 segundo
            initAlarm(m_AlarmId, 1000, 0, this);
        }
	else {
	    killAlarm(m_AlarmId);
	}
    }
    

    /**************************************************************
     *
     * UTILIDADES
     * 
     **************************************************************/


    public final int currentTimeSeconds() {
        return ((int)(System.currentTimeMillis() / 1000));
    }    

    public final String decoding(byte[] bytes, int start, int length) {
	return Utils.decoding(bytes, start, length, getAppEncoding());
    }

    public final byte[] encoding(final String string) {
	return Utils.encoding(string, getAppEncoding());
    }

    /**************************************************************
     *
     * CONFIGURACION
     * 
     **************************************************************/

    public final void appCfgLoadFromFile() {
        m_AppCfg.loadFromFile();
    }

    protected final boolean getAppCfgCommit() {
	return m_AppCfg.m_Commit;
    }

    protected final int getAppCfgSyncPer() {
	return m_AppCfg.m_SyncPer;
    }

    protected final int getAppCfgTimeOut() {
	return m_AppCfg.m_TimeOut;
    }

    protected final boolean getAppCfgBatch() {
	return m_AppCfg.m_Batch;
    }

    public final String getAppEncoding() {
	return m_AppCfg.m_Encoding;
    }

}
